﻿/*  ----------------------------------------------------------------------------
 *  SysServer
 *  ----------------------------------------------------------------------------
 *  File:       BoundedTQueue`1.cs
 *  Creater:     SEEN\MistSeen
 *  ----------------------------------------------------------------------------
 *  $Date: 2010-09-06 10:36:01 +0000 (Mon, 06 Sep 2010) $
 *  $Rev: 15 $
 *  $Author: ScorDeity $
 *  $URL: http://sysserver.googlecode.com/svn/trunk/src/SysServer/Collections/Generic/BoundedTQueue%601.cs $
 *  $Id: BoundedTQueue`1.cs 15 2010-09-06 10:36:01Z ScorDeity $
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace SysServer.Collections.Generic
{
    public class BoundedTQueue<T>
    {
        private TQueueNode<T> _Head;
        private TQueueNode<T> _Tail;
        private Semaphore _FreeSlots;
        private Semaphore _OccupiedSlots;

        public BoundedTQueue()
        {
            this._Head = this._Tail = new TQueueNode<T>();
            this._FreeSlots = this._OccupiedSlots = new Semaphore(0, int.MaxValue);
        }

        public BoundedTQueue(int itemCapacity)
        {
            if (itemCapacity < 1)
            {
                throw new ArgumentOutOfRangeException("capacity must be greater than 0");
            }
            this._Head = this._Tail = new TQueueNode<T>();
            this._FreeSlots = new Semaphore(itemCapacity, itemCapacity);
            this._OccupiedSlots = new Semaphore(0, itemCapacity);
        }
        public void Enqueue(T data)
        {
            TQueueNode<T> oldTail = null;
            TQueueNode<T> oldTailNext = null;
            TQueueNode<T> newNode = new TQueueNode<T>();

            newNode.Data = data;
            if (this._FreeSlots != null)
            {
                this._FreeSlots.WaitOne();
            }
            do
            {
                oldTail = this._Tail;
                oldTailNext = this._Tail.Next;

                if (oldTail == this._Tail)
                {
                    if (oldTailNext == null)
                    {
                        if (Interlocked.CompareExchange<TQueueNode<T>>(ref oldTail.Next, newNode, oldTailNext) == oldTailNext)
                        {
                            break;
                        }
                    }
                    else
                    {
                        Interlocked.CompareExchange<TQueueNode<T>>(ref this._Tail, oldTailNext, oldTail);
                    }
                }
            } while (true);

            Interlocked.CompareExchange<TQueueNode<T>>(ref this._Tail, newNode, oldTail);

            this._OccupiedSlots.Release();
        }

        public T Dequeue()
        {
            var date = default(T);
            TQueueNode<T> oldTail = null;
            TQueueNode<T> oldHead = null;
            TQueueNode<T> oldHeadNext = null;

            do
            {
                oldTail = this._Tail;
                oldHead = this._Head;
                oldHeadNext = oldHead.Next;

                if (oldHead == this._Head)
                {
                    if (oldHead != oldTail)
                    {
                        if (oldHead == null)
                        {
                            this._OccupiedSlots.WaitOne();
                            continue;
                        }
                        Interlocked.CompareExchange<TQueueNode<T>>(ref this._Tail, oldHeadNext, oldTail);
                    }
                    else
                    {
                        if (Interlocked.CompareExchange<TQueueNode<T>>(ref this._Head, oldHeadNext, oldHead) == oldHead)
                        {
                            date = oldHeadNext.Data;
                            break;
                        }

                    }
                }
            } while (true);
            oldHead.Data = default(T);

            if (this._FreeSlots != null)
            {
                this._FreeSlots.Release();
            }

            return date;
        }
    }
}